﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using GameFramework;
using UnityEditor;
using UnityEngine;

namespace NKG.Editor
{
    internal class OpcodeInfo
    {
        public string Name;
        public int Opcode;
    }

    public class Proto2CSEditor : EditorWindow
    {
        private const string protoPath = "Assets/GameMain/Configs/ProtoDefine/";
        private const string clientMessagePath = "Assets/GameMain/Scripts/NetWork/ETNetwork/MessageOutput/";
        private const string hotfixMessagePath = "Assets/GameMain/Scripts/NetWork/ETNetwork/MessageOutput/";
        private static readonly char[] splitChars = {' ', '\t'};
        private static readonly List<OpcodeInfo> msgOpcode = new List<OpcodeInfo>();

        [MenuItem("Tools/Proto2CS")]
        public static void AllProto2CS()
        {
            // InnerMessage.proto生成cs代码
            InnerProto2CS.Proto2CS();

            msgOpcode.Clear();
            ETServerProto2CS("ETModel", "ETServerOuterMessage.proto", @"D:\Study\GameProject\NKGFramework\NKGFramework\Server\Model\Module\Message\", "ETServerOuterOpcode", 100);

            msgOpcode.Clear();
            ETServerProto2CS("ETHotfix", "ETServerHotfixMessage.proto", @"D:\Study\GameProject\NKGFramework\NKGFramework\Server\Hotfix\Module\Message\", "ETServerHotfixOpcode", 10000);
            
            msgOpcode.Clear();
            ClientProto2CS("NKG.NetWork", "ClientHotfixMessage.proto", hotfixMessagePath, "ClientHotfixOpcode", 10000);

            CommandRun("protoc.bat", "");

            AssetDatabase.Refresh();
        }


        public static void CommandRun(string exe, string arguments)
        {
                ProcessStartInfo info = new ProcessStartInfo
                {
                    CreateNoWindow = true,
                    FileName = exe,
                    Arguments = arguments,
                    UseShellExecute = true,
                };
                Process p = Process.Start(info);
                p.WaitForExit();
        }

        public static void ETServerProto2CS(string ns, string protoName, string outputPath, string opcodeClassName,
            int startOpcode, bool isClient = true)
        {
            msgOpcode.Clear();
            string proto = Path.Combine(protoPath, protoName);

            string s = File.ReadAllText(proto);

            StringBuilder sb = new StringBuilder();
            sb.Append("using ETModel;\n");
            sb.Append($"namespace {ns}\n");
            sb.Append("{\n");

            bool isMsgStart = false;

            foreach (string line in s.Split('\n'))
            {
                string newline = line.Trim();

                if (newline == "")
                {
                    continue;
                }

                if (newline.StartsWith("//"))
                {
                    sb.Append($"{newline}\n");
                }

                if (newline.StartsWith("message"))
                {
                    string parentClass = "";
                    isMsgStart = true;
                    string msgName = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[1];
                    string[] ss = newline.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries);

                    if (ss.Length == 2)
                    {
                        parentClass = ss[1].Trim();
                    }
                    else
                    {
                        parentClass = "";
                    }

                    msgOpcode.Add(new OpcodeInfo() {Name = msgName, Opcode = ++startOpcode});

                    sb.Append($"\t[Message({opcodeClassName}.{msgName})]\n");
                    sb.Append($"\tpublic partial class {msgName} ");
                    if (parentClass != "")
                    {
                        sb.Append($": {parentClass} ");
                    }

                    sb.Append("{}\n\n");
                }

                if (isMsgStart && newline == "}")
                {
                    isMsgStart = false;
                }
            }

            sb.Append("}\n");

            GenerateOpcode(ns, opcodeClassName, outputPath, sb);
        }

        public static void ClientProto2CS(string ns, string protoName, string outputPath, string opcodeClassName,
            int startOpcode, bool isClient = true)
        {
            msgOpcode.Clear();
            string proto = Path.Combine(protoPath, protoName);

            string s = File.ReadAllText(proto);

            StringBuilder sb = new StringBuilder();
            sb.Append("using NKG.NetWork;\n");
            sb.Append($"namespace {ns}\n");
            sb.Append("{\n");

            bool isMsgStart = false;

            foreach (string line in s.Split('\n'))
            {
                string newline = line.Trim();

                if (newline == "")
                {
                    continue;
                }

                if (newline.StartsWith("//"))
                {
                    sb.Append($"{newline}\n");
                }

                if (newline.StartsWith("message"))
                {
                    string parentClass = "";
                    isMsgStart = true;
                    string msgName = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[1];
                    string[] ss = newline.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries);

                    if (ss.Length == 2)
                    {
                        parentClass = ss[1].Trim();
                    }
                    else
                    {
                        parentClass = "";
                    }

                    msgOpcode.Add(new OpcodeInfo() {Name = msgName, Opcode = ++startOpcode});

                    sb.Append($"\tpublic partial class {msgName} ");
                    if (parentClass != "")
                    {
                        sb.Append($": {parentClass} \n");
                    }

                    sb.Append("\t{\n");
                    sb.Append("\t\tpublic override int Id => " + opcodeClassName + '.' + msgName + ";\n");
                    sb.Append("\t\tpublic override void Clear()" + "\n");
                    sb.Append("\t\t{\n\n\t\t}\n");
                    sb.Append("\t}\n");
                }

                if (isMsgStart && newline == "}")
                {
                    isMsgStart = false;
                }
            }

            sb.Append("}\n");

            GenerateOpcode(ns, opcodeClassName, outputPath, sb);
        }

        private static void GenerateOpcode(string ns, string outputFileName, string outputPath, StringBuilder sb)
        {
            sb.AppendLine($"namespace {ns}");
            sb.AppendLine("{");
            sb.AppendLine($"\tpublic static partial class {outputFileName}");
            sb.AppendLine("\t{");
            foreach (OpcodeInfo info in msgOpcode)
            {
                sb.AppendLine($"\t\t public const ushort {info.Name} = {info.Opcode};");
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            string csPath = Path.Combine(outputPath, outputFileName + ".cs");
            File.WriteAllText(csPath, sb.ToString());
        }
    }
}